#!/usr/bin/env python
# -*- coding: utf-8 -*-

import re
import time

from tcpconn import *
from threading import Lock, Thread
from logger import log

from cmd_processor import *

class sequencer_thread(Thread):
    def __init__(self, sequencer):
        self._sequencer = sequencer
        Thread.__init__(self)
        
    def run(self):
        while not self._sequencer.stop:
            try:
                self._sequencer.tick(1.0)
            except socketException as e:
                self._sequencer.conn_error(e)
                self._sequencer.stop = True
        log("sequencer end")

class sequencer(object):
    
    _fytion = None
    _conn = None
    stop = False
    
    def __init__(self, fytion, conn):
        self._conn = conn
        self._lock = Lock()
        self._fytion = fytion
        self._recv_data = ''
        self._processors_client = {}
        self._processors_service = {}
        self._keepbusy_timer = time.time()
        self.stop = False
    
    def conn_error(self, e):
        self._fytion.seq_conn_closed(e)
    
    def close(self, e = None):
        self.stop = True
        self._conn.close()
        self._fytion.seq_conn_closed(e)
    
    def process_data(self, datastr):
        ret = {}
        t = datastr.find('\r\n')
        if t<0:
            return ret
        title = datastr[:t]
        datastr = datastr[t+2:]
        m = re.match(r"^(\S+) (\d{3}) ([a-zA-Z ]+)$", title)
        if m:
            ret['version'] = m.group(1)
            ret['status'] = int(m.group(2))
            ret['text'] = m.group(3)
            ret['is_response'] = True
        else:
            m = re.match(r"^([a-zA-Z]{1,3}) (\S+) (\S+)$", title)
            if not m:
                # packet error!
                log("Malformed packet.")
                return ret
            ret['version'] = m.group(3)
            ret['uri'] = m.group(2)
            ret['method'] = m.group(1)
            ret['is_response'] = False
        
        ret['headers'] = {}
        t = datastr.find('\r\n')
        while t > 0:
            line = datastr[:t]
            m = re.match(r"^([a-zA-Z]{1,3})\s*:\s*(.+)$", line)
            if not m:
                break
            ret['headers'][m.group(1)] = m.group(2)
            datastr = datastr[t+2:]
            t = datastr.find('\r\n')

        if t == 0:
            datastr = datastr[2:]
            if ret['headers'].has_key('L'):
                ret['body_lost_length'] = int(ret['headers']['L']) - len(datastr)
                ret['body'] = datastr[:int(ret['headers']['L'])]
            else:
                ret['body_lost_length'] = - len(datastr)
                ret['body'] = ''
        else:
            ret['body'] = ''
            ret['body_lost_length'] = 0
        return ret
    
    def register_client(self, id, processor):
        while self._processors_client.has_key(id):
            id += 1
        self._processors_client[id] = processor

    def deregister_client(self, id, processor):
        while not self._processors_client.has_key(id) or self._processors_client[id] != processor:
            id += 1
        if self._processors_client.has_key(id):
            del self._processors_client[id]

    def register_service(self, id, processor):
        while self._processors_service.has_key(id):
            id += 1
        self._processors_service[id] = processor

    def deregister_service(self, id, processor):
        while not self._processors_service.has_key(id) or self._processors_service[id] != processor:
            id += 1
        if self._processors_service.has_key(id):
            del self._processors_service[id]
        
    def dispatch(self, datastr):
        try:
            self._conn.send(datastr)
        except socketException as e:
            self.conn_error(e)
        self._keepbusy_timer = time.time()
        
    def tick(self, timeout = 0.0):
        if time.time() - self._keepbusy_timer > 20:
            self._fytion.keepbusy_seq(self)

        if self._lock and not self._lock.acquire(False):
            return
            
        d = self._recv_data
        self._recv_data = ''
        d += self._conn.recv(timeout)
        if not d:
            if self._lock:
                self._lock.release()
            if timeout==None:
                raise socketException()
            else:
                return
        
        t = d.find('\r\n\r\n')
        while t < 0:
            d += self._conn.recv(10.0)
            t = d.find('\r\n\r\n')
            
        pkt = self.process_data(d)
        r = pkt['body_lost_length']
        while r > 0:
            d1 = self._conn.recv(10.0)
            d += d1
            r -= len(d1)

        pkt = self.process_data(d)
        r = pkt['body_lost_length']
        if r < 0:
            self._recv_data = d[r:]
        
        if pkt['headers'].has_key('I'):
            id = int(pkt['headers']['I'])
            if pkt['is_response']:
                r = 1
                while self._processors_client.has_key(id) and r:
                    r = self._processors_client[id].recv_pkt(pkt)
                    id += 1
                if r:
                    log("No response processor want this packet.")
                    log(pkt)
            else:
                r = 1
                while self._processors_service.has_key(id) and r:
                    r = self._processors_service[id].recv_pkt(pkt)
                    id += 1
                if r:
                    proc = Processor.get_processor(self._fytion, self, pkt)
                    if proc:
                        r = proc.recv_pkt(pkt)
                if r:
                    log("No response processor want this packet.")
                    log(pkt)

        if self._lock:
            self._lock.release()
        return
        
    def background_tick(self):
        t = sequencer_thread(self)
        t.start()
        
